﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Hook.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Hook.Graphics.SpriteSheets;
using Hook.Environmental;
using Hook.Environmental.Props;

namespace Hook.Primitives
{
    public class Line
    {
        public const float FLOATGRACE = .1f;

        public Sprite Sprite;
        public PolyPoint Start, End;
        public Vector3 Dimension;
        public float YSlope, XSlope, YIntercept;

        public Line(PolyPoint Start, PolyPoint End)
        {
            Sprite = new Sprite(Game1.PrimitiveSheet, PrimitiveSheet.LINEPIECE);
            this.Start = Start;
            this.End = End;
            Dimension.X = (float)Math.Round(End.Position.X - Start.Position.X);
            Dimension.Y = (float)Math.Round(End.Position.Y - Start.Position.Y);
            YSlope = Dimension.Y / Dimension.X;
            XSlope = Dimension.X / Dimension.Y;
            YIntercept = YSlope * -Start.Position.X + Start.Position.Y;
        }

        public void Update(GameTime gt)
        {
            Dimension.X = (End.Position.X - Start.Position.X);
            Dimension.Y = (End.Position.Y - Start.Position.Y);
            YSlope = Dimension.Y / Dimension.X;
            XSlope = Dimension.X / Dimension.Y;
            YIntercept = YSlope * -Start.Position.X + Start.Position.Y;

            Sprite.Position = Start.Position;
            Sprite.Update(gt);
        }
        public void Draw(SpriteBatch sb, Camera Cam)
        {
            if(Math.Abs(Dimension.X) >= Math.Abs(Dimension.Y))
            {
                if(Start.Position.X < End.Position.X)
                {
                    for(int x = 0; x < (int)End.Position.X - (int)Start.Position.X; x++)
                    {
                        Sprite.Position = new Vector3(Start.Position.X + x, Start.Position.Y + x * YSlope, 0);
                        Sprite.Draw(sb, Cam);
                    }
                }
                else
                {
                    for(int x = 0; x > (int)End.Position.X - (int)Start.Position.X; x--)
                    {
                        Sprite.Position = new Vector3(Start.Position.X + x, Start.Position.Y + x * YSlope, 0);
                        Sprite.Draw(sb, Cam);
                    }
                }
            }
            else
            {
                if(Start.Position.Y < End.Position.Y)
                {
                    for(int y = 0; y < (int)End.Position.Y - (int)Start.Position.Y; y++)
                    {
                        Sprite.Position = new Vector3(Start.Position.X + y * XSlope, Start.Position.Y + y, 0);
                        Sprite.Draw(sb, Cam);
                    }
                }
                else
                {
                    for(int y = 0; y > (int)End.Position.Y - (int)Start.Position.Y; y--)
                    {
                        Sprite.Position = new Vector3(Start.Position.X + y * XSlope, Start.Position.Y + y, 0);
                        Sprite.Draw(sb, Cam);
                    }
                }
            }
        }

        public Vector2 Perpendicular()
        {
            Vector3 LineVector = End.Position - Start.Position;
            Vector2 PerpVector = new Vector2(LineVector.Y, -LineVector.X);
            return PerpVector;
        }

        public Vector3 PointDistance(PolyPoint P)
        {
            Vector3 HypotenuseLight = P.Position - Start.Position;
            Vector3 LineCanvas = End.Position - Start.Position;
            Vector3 ShadowDistance = Prop.Projection(LineCanvas, HypotenuseLight);

            return HypotenuseLight - ShadowDistance;

            /*Vector3 H = P.Position - Start.Position;
            Vector3 O = PointProjection(P);
            Vector3 Distance = O - H;
            return Distance;*/
        }

        public Vector3 PointProjection(PolyPoint P)
        {
            Vector3 Canvas = End.Position - Start.Position;
            Vector3 Light = P.Position - Start.Position;

            Vector3 Shadow = Prop.Projection(Canvas, Light);
            return Shadow;
        }

        public bool BoundsPoint(PolyPoint p)
        {
            if(Dimension.X > 0)
            {
                if(p.Position.Y > YSlope * p.Position.X + YIntercept + FLOATGRACE)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else if(Dimension.X < 0)
            {
                if(p.Position.Y < YSlope * p.Position.X + YIntercept - FLOATGRACE)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                if(Dimension.Y > 0)
                {
                    if(p.Position.X < Start.Position.X - FLOATGRACE)
                        return true;
                    else
                        return false;
                }
                else if(Dimension.Y < 0)
                {
                    if(p.Position.X > Start.Position.X + FLOATGRACE)
                        return true;
                    else
                        return false;
                }
                else
                {
                    return false;
                }
            }
        }

        //Checks if point is within bounding rectangle of line
        public bool ContainsPoint(PolyPoint p)
        {
            float LeftBound, RightBound, HighBound, LowBound;
            if(Start.Position.X < End.Position.X)
            {
                LeftBound = Start.Position.X;
                RightBound = End.Position.X;
            }
            else
            {
                LeftBound = End.Position.X;
                RightBound = Start.Position.X;
            }
            if(Start.Position.Y < End.Position.Y)
            {
                HighBound = Start.Position.Y;
                LowBound = End.Position.Y;
            }
            else
            {
                HighBound = End.Position.Y;
                LowBound = Start.Position.Y;
            }
            if(p.Position.X >= LeftBound && p.Position.X <= RightBound
                && p.Position.Y >= HighBound && p.Position.Y <= LowBound)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public PolyPoint Intersection(Line l)
        {
            float Scalar, YFactor, YConstant, YSolution, XSolution;
            
            // Handle parallel awkward slopes
            if(YSlope == 0 && l.YSlope == 0)
            {
                YSolution = XSolution = 0;
            }
            else if(float.IsInfinity(YSlope) && float.IsInfinity(l.YSlope))
            {
                YSolution = XSolution = 0;
            }

            // Handle one slope infinity, one slope zero
            else if(float.IsInfinity(YSlope) && l.YSlope == 0)
            {
                XSolution = Start.Position.X;
                YSolution = l.YIntercept;
            }
            else if(YSlope == 0 && float.IsInfinity(l.YSlope))
            {
                XSolution = l.Start.Position.X;
                YSolution = YIntercept;
            }

            // Handle single 0 or infinite slopes
            else if(YSlope == 0)
            {
                YSolution = YIntercept;
                XSolution = (YSolution - l.YIntercept) / l.YSlope;
            }
            else if(l.YSlope == 0)
            {
                YSolution = l.YIntercept;
                XSolution = (YSolution - YIntercept) / YSlope;
            }
            else if(float.IsInfinity(YSlope))
            {
                XSolution = Start.Position.X;
                YSolution = l.YSlope * XSolution + l.YIntercept;
            }
            else if(float.IsInfinity(l.YSlope))
            {
                XSolution = l.Start.Position.X;
                YSolution = YSlope * XSolution + YIntercept;
            }

            // Otherwise do the usual
            else
            {
                Scalar = -l.YSlope / YSlope;
                YFactor = Scalar + 1;
                YConstant = l.YIntercept + Scalar * YIntercept;
                YSolution = YConstant / YFactor;
                XSolution = (YSolution - YIntercept) / YSlope;
            }

            return new PolyPoint(new Vector3(XSolution, YSolution, 0));
        }

        /// <summary>
        /// Returns a point that is shared between two given lines, or null if a shared point does not exist.
        /// </summary>
        public static PolyPoint CommonPoint(Line L1, Line L2)
        {
            if (L1.Start == L2.Start)
            {
                return L1.Start;
            }
            else if (L1.Start == L2.End)
            {
                return L1.Start;
            }
            else if (L1.End == L2.Start)
            {
                return L1.End;
            }
            else if (L1.End == L2.End)
            {
                return L1.End;
            }
            else
            {
                return null;
            }
        }
    }
}
